{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Live Update In-Progress"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "np.argsort()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "SCOPETYPE = 'OPENADC'\n",
    "PLATFORM = 'CWLITEARM'\n",
    "CRYPTO_TARGET = 'NONE'\n",
    "sample_size = 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "%run \"../Setup_Scripts/Setup_Generic.ipynb\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash -s \"$PLATFORM\"\n",
    "cd ../../firmware/mcu/simpleserial-glitch\n",
    "make PLATFORM=$1 CRYPTO_TARGET=NONE"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cw.program_target(scope, prog, \"../../firmware/mcu/simpleserial-glitch/simpleserial-glitch-{}.hex\".format(PLATFORM))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import logging\n",
    "\n",
    "#debug - delete this 💩\n",
    "\n",
    "target.flush()\n",
    "target.write(\"g\\n\")\n",
    "r = target.simpleserial_read_witherrors(\"r\", 4)\n",
    "print(r)\n",
    "\n",
    "\n",
    "scope.io.nrst = False\n",
    "scope.io.nrst = True\n",
    "\n",
    "r = target.simpleserial_read_witherrors(\"r\", 1)\n",
    "print(r)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#debug - delete this 💩\n",
    "\n",
    "from ipywidgets import interact, widgets\n",
    "from IPython.display import display\n",
    "import time\n",
    "\n",
    "text = widgets.Text(\n",
    "    value='cA3',\n",
    "    placeholder='cA3',\n",
    "    description='Serial TX',\n",
    "    disabled=False\n",
    ")\n",
    "display(text)\n",
    "\n",
    "def callback(wdgt):\n",
    "    sent = wdgt.value + \"\\n\"\n",
    "    target.write(sent)\n",
    "    print(sent)\n",
    "\n",
    "text.on_submit(callback)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "import time\n",
    "\n",
    "#debug - delete this 💩\n",
    "\n",
    "target.read(100)\n",
    "\n",
    "scope.io.nrst = False\n",
    "time.sleep(0.1)\n",
    "\n",
    "scope.io.target_pwr = False\n",
    "time.sleep(0.5)\n",
    "scope.io.target_pwr = True\n",
    "time.sleep(1)\n",
    "\n",
    "\n",
    "scope.io.nrst = None\n",
    "time.sleep(1.5)\n",
    "\n",
    "target.read(100)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Basic setup\n",
    "scope.glitch.clk_src = \"clkgen\"\n",
    "scope.glitch.output = \"clock_xor\"\n",
    "scope.glitch.trigger_src = \"ext_single\"\n",
    "\n",
    "scope.io.hs2 = \"glitch\"  \n",
    "print(scope.glitch)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def reboot_flush():            \n",
    "    scope.io.nrst = False\n",
    "    time.sleep(0.1)\n",
    "    scope.io.nrst = True\n",
    "    time.sleep(0.1)\n",
    "    #Flush garbage too\n",
    "    target.read(100)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# GlitchController will be part of ChipWhisperer core - just run this block\n",
    "# for now.\n",
    "\n",
    "%matplotlib notebook\n",
    "import matplotlib.pylab as plt\n",
    "\n",
    "import ipywidgets as widgets          \n",
    "import chipwhisperer.common.results.glitch as glitch\n",
    "\n",
    "class GlitchController(object):\n",
    "    \n",
    "    def __init__(self, groups, parameters):\n",
    "        self.groups = groups\n",
    "        self.parameters = parameters\n",
    "        \n",
    "        self.results = glitch.GlitchResults(groups=groups, parameters=parameters)\n",
    "        \n",
    "        self.parameter_min = [0.0] * len(parameters)\n",
    "        self.parameter_max = [10.0] * len(parameters)\n",
    "        self.steps = [1]\n",
    "        \n",
    "        self.widget_list_parameter = None\n",
    "        self.widget_list_groups = None\n",
    "        \n",
    "        self.clear()\n",
    "        \n",
    "    def clear(self):\n",
    "        self.results.clear()        \n",
    "        self.group_counts = [0] * len(self.groups)\n",
    "        \n",
    "        if self.widget_list_groups:\n",
    "            for w in self.widget_list_groups:\n",
    "                w.value = 0\n",
    "        \n",
    "    def set_range(self, parameter, low, high):\n",
    "        \n",
    "        if high < low:\n",
    "            t = low\n",
    "            low = high\n",
    "            high = t\n",
    "        \n",
    "        i = self.parameters.index(parameter)\n",
    "        self.parameter_min[i] = low\n",
    "        self.parameter_max[i] = high\n",
    "        \n",
    "        if self.widget_list_parameter:\n",
    "            # When changing them, need to ensure we don't have min > max ever or will throw\n",
    "            # an error, so we set max to super-high first.\n",
    "            self.widget_list_parameter[i].max = 1E9\n",
    "            self.widget_list_parameter[i].min = low\n",
    "            self.widget_list_parameter[i].max = high\n",
    "    \n",
    "    def set_step(self, parameter, step):\n",
    "        '''Set size, can be either a single value or a list of step-sizes to iterate through'''\n",
    "        raise NotImplementedError(\"TODO\")\n",
    "        \n",
    "    def set_global_step(self, steps):\n",
    "        '''Set step for all parameters (Eventually support per-parameter)'''\n",
    "        if hasattr(steps, \"__iter__\"):\n",
    "            self.steps = steps\n",
    "        else:\n",
    "            self.steps = [steps]\n",
    "    \n",
    "    def add(self, group, parameters, strdesc=None, metadata=None):\n",
    "        self.results.add(group, parameters, strdesc, metadata)    \n",
    "        \n",
    "        i = self.groups.index(group)        \n",
    "        #Basic count\n",
    "        self.group_counts[i] += 1\n",
    "        self.widget_list_groups[i].value =  self.group_counts[i]\n",
    "    \n",
    "    def display_stats(self):\n",
    "        \n",
    "        self.widget_list_groups = [widgets.IntText(value=0, description=group + \" count:\", disabled=True)\n",
    "                                   for group in self.groups]\n",
    "        \n",
    "        self.widget_list_parameter = [widgets.FloatSlider(\n",
    "                                            value=self.parameter_min[i],\n",
    "                                            min=self.parameter_min[i],\n",
    "                                            max=self.parameter_max[i],\n",
    "                                            step=0.01,\n",
    "                                            description=p + \" setting:\",\n",
    "                                            disabled=True,\n",
    "                                            continuous_update=False,\n",
    "                                            orientation='horizontal',\n",
    "                                            readout=True,\n",
    "                                            readout_format='.01f')\n",
    "                                          for i,p in enumerate(self.parameters)]\n",
    "            \n",
    "        display(*(self.widget_list_groups + self.widget_list_parameter))\n",
    "       \n",
    "        \n",
    "    def glitch_values(self, clear=True):\n",
    "        \"\"\"Generator returning the given parameter values in order, using the step size (or step list)\"\"\"\n",
    "        \n",
    "        self.parameter_values = self.parameter_min[:]\n",
    "        \n",
    "        if clear:\n",
    "            self.clear()\n",
    "        \n",
    "        for stepsize in self.steps:\n",
    "            for val in self._loop_rec(0, len(self.parameter_values)-1, stepsize):\n",
    "                if self.widget_list_parameter:\n",
    "                    for i,v in enumerate(val):\n",
    "                        self.widget_list_parameter[i].value = v\n",
    "                yield val\n",
    "        \n",
    "    def _loop_rec(self, parameter_index, final_index, step):\n",
    "        self.parameter_values[parameter_index] = self.parameter_min[parameter_index]\n",
    "        if parameter_index == final_index:            \n",
    "            while self.parameter_values[parameter_index] <= self.parameter_max[parameter_index]:                                \n",
    "                yield self.parameter_values\n",
    "                self.parameter_values[parameter_index] += step\n",
    "        else:\n",
    "            while self.parameter_values[parameter_index] <= self.parameter_max[parameter_index]: \n",
    "                yield from self._loop_rec(parameter_index+1, final_index, step)\n",
    "                self.parameter_values[parameter_index] += step\n",
    "\n",
    "        \n",
    "gc = GlitchController(groups=[\"success\", \"reset\", \"normal\"], parameters=[\"width\", \"offset\"])\n",
    "gc.display_stats()\n",
    "#gc.display_controls()\n",
    "\n",
    "\n",
    "fig = plt.figure()\n",
    "plt.plot(-48, 48, ' ')\n",
    "plt.plot(48, -48, ' ')\n",
    "plt.plot(-48, -48, ' ')\n",
    "plt.plot(48, 48, ' ')\n",
    "#plt.ion()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ready to rock - run the following block which should update the stats above. Note you need to run stop on the block to stop anything... sometimes need to hit it a bunch of times. TOOD is adding a button to abort."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "\n",
    "from importlib import reload\n",
    "import chipwhisperer.common.results.glitch as glitch\n",
    "from tqdm import tnrange\n",
    "import struct\n",
    "\n",
    "scope.glitch.ext_offset = 0\n",
    "\n",
    "#Uncomment for the full sweep\n",
    "#gc.set_range(\"width\", -48, 48)\n",
    "#gc.set_range(\"offset\", -48, 48)\n",
    "#gc.set_global_step([4.0, 0.8])\n",
    "#scope.glitch.repeat = 50\n",
    "\n",
    "#Uncomment for upper left region\n",
    "gc.set_range(\"width\", -48, 48)\n",
    "gc.set_range(\"offset\", -48, 48)\n",
    "gc.set_global_step([8.0, 4.0, 2.0, 1.0])\n",
    "scope.glitch.repeat = 8 #4\n",
    "\n",
    "#Uncomment for more focused region\n",
    "#gc.set_range(\"width\", 7, 8)\n",
    "#gc.set_range(\"offset\", -24, -22)\n",
    "#gc.set_global_step([4.0, 0.8, 0.25, 0.1])\n",
    "#scope.glitch.repeat = 5\n",
    "\n",
    "sample_size = 1\n",
    "\n",
    "for glitch_setting in gc.glitch_values():\n",
    "    scope.glitch.offset = glitch_setting[1]\n",
    "    scope.glitch.width = glitch_setting[0]\n",
    "    for i in tnrange(sample_size, leave=False):\n",
    "        scope.arm()\n",
    "        #reset_target(scope)\n",
    "        #target.write(\"cA2\\n\") #send A2 is CORRECT\n",
    "        #target.write(\"cA3\\n\") #send A3 is INCORRECT\n",
    "        \n",
    "        #Do glitch loop\n",
    "        target.write(\"g\\n\")\n",
    "        \n",
    "        ret = scope.capture()\n",
    "        \n",
    "        \n",
    "        #val = target.simpleserial_read_witherrors('r', 1)#For code-flow check\n",
    "        \n",
    "        \n",
    "        val = target.simpleserial_read_witherrors('r', 4)#For loop check\n",
    "        \n",
    "        if ret:\n",
    "            print('Timeout - no trigger')\n",
    "            gc.add(\"reset\", (scope.glitch.width, scope.glitch.offset))\n",
    "            plt.plot(scope.glitch.width, scope.glitch.offset, 'xr', alpha=1)\n",
    "            fig.canvas.draw()\n",
    "\n",
    "            #Device is slow to boot?\n",
    "            reboot_flush()\n",
    "\n",
    "        else:\n",
    "            if val['valid'] is False:\n",
    "                gc.add(\"reset\", (scope.glitch.width, scope.glitch.offset))\n",
    "                print(\"x\")\n",
    "            else:\n",
    "                \n",
    "                #print(val['payload'])\n",
    "                \n",
    "                #gcnt = struct.unpack(\"<b\", val['payload'])[0] #for code-flow check\n",
    "                gcnt = struct.unpack(\"<I\", val['payload'])[0]\n",
    "\n",
    "                #print(gcnt)                \n",
    "                # for table display purposes\n",
    "                #if gnt != 0: #for code-flow check\n",
    "                if gcnt != 2500: #for loop check\n",
    "                    gc.add(\"success\", (scope.glitch.width, scope.glitch.offset))\n",
    "                    plt.plot(scope.glitch.width, scope.glitch.offset, '+g')\n",
    "                    print(val['payload'])\n",
    "                    print(\"🐙\", end=\"\")\n",
    "                else:\n",
    "                    gc.add(\"normal\", (scope.glitch.width, scope.glitch.offset))\n",
    "                    plt.plot(scope.glitch.width, scope.glitch.offset, '.k', alpha=0.1)\n",
    "                    fig.canvas.draw()\n",
    "                    \n",
    "\n",
    "print(\"Done glitching\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib notebook\n",
    "import matplotlib.pylab as plt\n",
    "\n",
    "# plot_2d is part of CW core, but has some bugs with huge data (like generated here) where you don't\n",
    "# want to plot everything. For now just use the following which replaces the plot_2d function with some bug-fixes\n",
    "\n",
    "def plot_2d(self, plotdots, x_index=0, y_index=1, x_units=None, y_units=None, mask=True):\n",
    "    '''\n",
    "    Generate a 2D plot of glitch success rate using matplotlib.\n",
    "\n",
    "    Plotting is done in the default figure - you may need to call plt.figure() before and\n",
    "    plt.show() after calling this function if you want more control (or the figure does\n",
    "    not show by default).\n",
    "    '''\n",
    "\n",
    "    data = self.calc()\n",
    "\n",
    "    #We only want legend to show for first element... bit of a hack here\n",
    "    legs = self.groups[:]\n",
    "\n",
    "    #Generate success rates\n",
    "    for p in data:\n",
    "        #Plot based on non-zero priority if possible\n",
    "        for g in self.groups:\n",
    "            if plotdots[g]:\n",
    "                if p[g] > 0:\n",
    "                    if g in legs:\n",
    "                        leg = g.title()\n",
    "                        #No need to show this one anymore\n",
    "                        legs.remove(g)\n",
    "                    else:\n",
    "                        leg = None\n",
    "\n",
    "                    sr = float(p[g]) / float(p['_total']) + 0.5\n",
    "                    \n",
    "                    if sr > 1.0:\n",
    "                        sr = 1.0\n",
    "                    \n",
    "                    plt.plot(p['_parameter'][x_index], p['_parameter'][y_index], plotdots[g], alpha=sr, label=leg)\n",
    "\n",
    "                    if mask:\n",
    "                        break\n",
    "\n",
    "    xlabel = self.parameters[x_index].title()\n",
    "    if x_units:\n",
    "        xlabel += \" (\" + x_units + \")\"\n",
    "    plt.xlabel(xlabel)\n",
    "\n",
    "    ylabel = self.parameters[y_index].title()\n",
    "    if y_units:\n",
    "        ylabel += \" (\" + y_units + \")\"\n",
    "    plt.ylabel(ylabel)\n",
    "\n",
    "    plt.legend()\n",
    "\n",
    "# Plot the results. If you have no glitches or resets this will generat an error -\n",
    "# this is expected behaviour as we also tell it not to plot the \"normal\" responses.\n",
    "plot_2d(gc.results, plotdots={\"success\":\"+g\", \"reset\":\"xr\", \"normal\":None})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.io.target_pwr = True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#width -48 to -30\n",
    "#offset 40 to 48"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "python -m jupyter nbextension enable --py widgetsnbextension"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.adc.trig_count"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
